<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue属性绑定详解与示例</title>
    <script src="https://unpkg.com/vue@3.4.0"></script>
    <style>
      body {
        font-family: 'Consolas', '微软雅黑';
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
        background-color: #f5f5f5;
        line-height: 1.6;
      }
      code {
        font-family: 'Consolas', '微软雅黑';
      }
      .container {
        background: white;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        margin-bottom: 20px;
      }
      h1 {
        color: #2c3e50;
        text-align: center;
        margin-bottom: 30px;
        font-size: 2.5em;
      }
      h2 {
        color: #34495e;
        border-bottom: 3px solid #3498db;
        padding-bottom: 10px;
        margin-top: 30px;
      }
      h3 {
        color: #2980b9;
        margin-top: 25px;
      }
      .demo-section {
        margin: 20px 0;
        padding: 20px;
        border: 1px solid #ddd;
        border-radius: 8px;
        background-color: #f9f9f9;
      }
      .code-block {
        background-color: #2d3748;
        border-radius: 8px;
        padding: 0;
        margin: 15px 0;
        overflow: hidden;
      }
      .code-block textarea {
        height: auto;
        min-height: 300px;
        font-family: 'Consolas', '微软雅黑';
        font-size: 13px;
        padding: 15px;
        border: none;
        border-radius: 0;
        background-color: transparent;
        resize: none;
        line-height: 1.5;
        box-sizing: border-box;
        color: #e2e8f0;
        width: 100%;
        outline: none;
      }
      .image-demo img {
        max-width: 200px;
        margin: 10px;
        border-radius: 8px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      }
      .link-demo a {
        margin: 10px;
        display: inline-block;
        color: #3498db;
        text-decoration: none;
        padding: 8px 16px;
        border: 1px solid #3498db;
        border-radius: 4px;
        transition: all 0.3s;
      }
      .link-demo a:hover {
        background-color: #3498db;
        color: white;
      }
      .input-demo input, .input-demo select {
        margin: 5px;
        padding: 8px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
      }
      button {
        background-color: #3498db;
        color: white;
        padding: 8px 16px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin: 5px;
        transition: background-color 0.3s;
      }
      button:hover {
        background-color: #2980b9;
      }
      button:disabled {
        background-color: #95a5a6;
        cursor: not-allowed;
      }
      .highlight {
        background-color: #fff3cd;
        padding: 2px 4px;
        border-radius: 3px;
      }
      .performance-note {
        background: #e8f4fd;
        padding: 15px;
        border-radius: 5px;
        border-left: 4px solid #2196f3;
        margin: 15px 0;
      }
      .warning {
        background: #fff3cd;
        padding: 15px;
        border-radius: 5px;
        border-left: 4px solid #ffc107;
        color: #856404;
      }
      .info {
        background: #d1ecf1;
        padding: 15px;
        border-radius: 5px;
        border-left: 4px solid #17a2b8;
        color: #0c5460;
      }
      label {
        display: inline-block;
        margin: 5px 10px;
        cursor: pointer;
      }
      .multi-attrs-demo {
        padding: 10px;
        margin: 10px 0;
        border-radius: 4px;
        transition: all 0.3s;
      }
    </style>
  </head>
  <body>
    <h1>Vue属性绑定详解与示例</h1>

    <!-- 概述 -->
    <div class="container">
      <h2>概述</h2>
      <p>Vue.js的属性绑定是构建动态用户界面的基础功能。通过<code>v-bind</code>指令（简写为<code>:</code>），我们可以动态地绑定HTML属性到Vue实例的数据。主要包括：</p>
      <ul>
        <li><span class="highlight">v-bind</span>：将HTML属性绑定到Vue数据</li>
        <li><span class="highlight">简写语法</span>：使用<code>:</code>代替<code>v-bind:</code></li>
        <li><span class="highlight">布尔属性</span>：处理disabled、checked等布尔类型属性</li>
        <li><span class="highlight">多属性绑定</span>：一次性绑定多个属性</li>
        <li><span class="highlight">动态属性名</span>：属性名本身也可以是动态的</li>
      </ul>
    </div>

    <!-- 1. 基础属性绑定 -->
    <div class="container">
      <h2>1. 基础属性绑定</h2>
      <p>最常见的属性绑定包括图片的src、链接的href、表单元素的各种属性等。</p>
      
      <div class="demo-section" id="basic-binding-demo">
        <h3>图片src绑定演示</h3>
        <div class="image-demo">
          <p>动态图片：</p>
          <img v-bind:src="currentImage" v-bind:alt="imageAlt" />
          <br />
          <button @click="changeImage">切换图片</button>
        </div>
      </div>

      <div class="code-block">
        <textarea readonly>
<!-- HTML模板 -->
<img v-bind:src="currentImage" v-bind:alt="imageAlt" />
<!-- 简写形式 -->
<img :src="currentImage" :alt="imageAlt" />
<button @click="changeImage">切换图片</button>

// JavaScript代码
const { createApp, ref, computed } = Vue

const app = createApp({
  setup() {
    const images = [
      'https://picsum.photos/200/150?random=1',
      'https://picsum.photos/200/150?random=2', 
      'https://picsum.photos/200/150?random=3'
    ]
    const currentImageIndex = ref(0)
    const currentImage = computed(() => images[currentImageIndex.value])
    const imageAlt = computed(() => `示例图片 ${currentImageIndex.value + 1}`)
    
    const changeImage = () => {
      currentImageIndex.value = (currentImageIndex.value + 1) % images.length
    }
    
    return { currentImage, imageAlt, changeImage }
  }
})
app.mount('#basic-binding-demo')
        </textarea>
      </div>

      <div class="performance-note">
        <strong>重要提示：</strong>v-bind可以简写为冒号(:)，这是Vue开发中的常用写法。
      </div>
    </div>

    <!-- 2. 链接和表单属性绑定 -->
    <div class="container">
      <h2>2. 链接和表单属性绑定</h2>
      <p>动态绑定链接的href属性和表单元素的各种属性，实现交互式的用户界面。</p>
      
      <div class="demo-section" id="link-form-demo">
        <h3>动态链接演示</h3>
        <div class="link-demo">
          <p>动态链接：</p>
          <a :href="dynamicUrl" :target="linkTarget">{{linkText}}</a>
          <br />
          <input v-model="dynamicUrl" placeholder="输入URL" />
          <label>
            <input type="checkbox" v-model="openInNewTab" />
            新窗口打开
          </label>
        </div>

        <h3>表单属性绑定演示</h3>
        <div class="input-demo">
          <p>表单属性绑定：</p>
          <input
            :type="inputType"
            :placeholder="inputPlaceholder"
            :disabled="isDisabled"
            :readonly="isReadonly"
            v-model="inputValue"
          />
          <br />
          <select v-model="inputType">
            <option value="text">文本</option>
            <option value="password">密码</option>
            <option value="email">邮箱</option>
            <option value="number">数字</option>
          </select>
          <label>
            <input type="checkbox" v-model="isDisabled" />
            禁用
          </label>
          <label>
            <input type="checkbox" v-model="isReadonly" />
            只读
          </label>
        </div>
      </div>

      <div class="code-block">
        <textarea readonly>
<!-- 动态链接 -->
<a :href="dynamicUrl" :target="linkTarget">{{linkText}}</a>
<input v-model="dynamicUrl" placeholder="输入URL" />
<label>
  <input type="checkbox" v-model="openInNewTab" />
  新窗口打开
</label>

<!-- 表单属性绑定 -->
<input
  :type="inputType"
  :placeholder="inputPlaceholder"
  :disabled="isDisabled"
  :readonly="isReadonly"
  v-model="inputValue"
/>

// JavaScript代码
const app = createApp({
  setup() {
    // 链接相关
    const dynamicUrl = ref('https://vuejs.org')
    const openInNewTab = ref(false)
    const linkTarget = computed(() => openInNewTab.value ? '_blank' : '_self')
    const linkText = computed(() => `访问 ${dynamicUrl.value}`)
    
    // 表单相关
    const inputType = ref('text')
    const inputPlaceholder = computed(() => {
      const placeholders = {
        text: '请输入文本',
        password: '请输入密码',
        email: '请输入邮箱',
        number: '请输入数字'
      }
      return placeholders[inputType.value]
    })
    const isDisabled = ref(false)
    const isReadonly = ref(false)
    const inputValue = ref('')
    
    return {
      dynamicUrl, openInNewTab, linkTarget, linkText,
      inputType, inputPlaceholder, isDisabled, isReadonly, inputValue
    }
  }
})
app.mount('#link-form-demo')
        </textarea>
      </div>
    </div>

    <!-- 3. 布尔属性绑定 -->
    <div class="container">
      <h2>3. 布尔属性绑定</h2>
      <p>布尔属性在Vue中有特殊的处理方式。当绑定值为真值时，属性会被添加到元素上；为假值时，属性会被移除。</p>
      
      <div class="demo-section" id="boolean-demo">
        <h3>布尔属性演示</h3>
        <button :disabled="isButtonDisabled">{{isButtonDisabled ? '已禁用' : '可点击'}}</button>
        <input type="checkbox" :checked="isChecked" @change="isChecked = !isChecked" />
        <input type="text" :required="isRequired" placeholder="必填字段" />

        <br /><br />
        <label>
          <input type="checkbox" v-model="isButtonDisabled" />
          禁用按钮
        </label>
        <label>
          <input type="checkbox" v-model="isRequired" />
          必填字段
        </label>
      </div>

      <div class="code-block">
        <textarea readonly>
<!-- 布尔属性绑定 -->
<button :disabled="isButtonDisabled">{{isButtonDisabled ? '已禁用' : '可点击'}}</button>
<input type="checkbox" :checked="isChecked" @change="isChecked = !isChecked" />
<input type="text" :required="isRequired" placeholder="必填字段" />

// JavaScript代码
const app = createApp({
  setup() {
    const isButtonDisabled = ref(false)
    const isChecked = ref(false)
    const isRequired = ref(false)
    
    return {
      isButtonDisabled,
      isChecked,
      isRequired
    }
  }
})
app.mount('#boolean-demo')
        </textarea>
      </div>

      <div class="warning">
        <strong>注意：</strong>对于布尔属性，如果绑定值是假值（false、null、undefined），该属性会被移除。
      </div>
    </div>

    <!-- 4. 多个属性绑定 -->
    <div class="container">
      <h2>4. 多个属性绑定</h2>
      <p>使用v-bind不带参数可以绑定一个包含多个属性的对象。这在需要同时设置多个属性时非常有用。</p>
      
      <div class="demo-section" id="multiple-attrs-demo">
        <h3>多属性绑定演示</h3>
        <div v-bind="multipleAttrs" class="multi-attrs-demo">多属性绑定的div</div>
        <br />
        <button @click="updateAttrs">更新属性</button>
      </div>

      <div class="code-block">
        <textarea readonly>
<!-- 多属性绑定 -->
<div v-bind="multipleAttrs">多属性绑定的div</div>
<button @click="updateAttrs">更新属性</button>

// JavaScript代码
const app = createApp({
  setup() {
    const multipleAttrs = ref({
      id: 'multi-attrs',
      class: 'highlight',
      style: 'padding: 10px; background: lightblue;',
      'data-info': '多属性绑定示例'
    })
    
    const updateAttrs = () => {
      multipleAttrs.value = {
        ...multipleAttrs.value,
        style: `padding: 15px; background: ${Math.random() > 0.5 ? 'lightcoral' : 'lightgreen'};`,
        'data-info': `更新时间: ${new Date().toLocaleTimeString()}`
      }
    }
    
    return {
      multipleAttrs,
      updateAttrs
    }
  }
})
app.mount('#multiple-attrs-demo')
        </textarea>
      </div>

      <div class="info">
        <strong>提示：</strong>多属性绑定会将对象的所有属性作为HTML属性绑定到元素上。
      </div>
    </div>

    <!-- 5. 动态属性名 -->
    <div class="container">
      <h2>5. 动态属性名</h2>
      <p>Vue 3支持动态属性名，可以使用方括号语法来动态决定要绑定的属性名。</p>
      
      <div class="demo-section" id="dynamic-attr-demo">
        <h3>动态属性名演示</h3>
        <p>当前绑定的属性：<strong>{{attributeName}}</strong></p>
        <p>属性值：<strong>{{attributeValue}}</strong></p>
        <input :[attributeName]="attributeValue" placeholder="动态属性演示" style="margin: 10px 0; padding: 8px; border: 2px solid #ddd; border-radius: 4px;" />
        <br />
        <label>选择要绑定的属性：</label>
        <select v-model="attributeName" style="margin: 5px; padding: 5px;">
          <option value="placeholder">placeholder (占位符)</option>
          <option value="title">title (提示标题)</option>
          <option value="data-info">data-info (自定义数据)</option>
          <option value="maxlength">maxlength (最大长度)</option>
        </select>
        <br />
        <label>设置属性值：</label>
        <input v-model="attributeValue" placeholder="输入属性值" style="margin: 5px; padding: 5px;" />
        <br />
        <small style="color: #666; margin-top: 10px; display: block;">
          提示：选择不同属性后，在上方输入框中可以看到效果。hover到输入框上可以看到title效果。
        </small>
      </div>

      <div class="code-block">
        <textarea readonly>
<!-- 动态属性名 -->
<input :[attributeName]="attributeValue" placeholder="动态属性演示" />
<select v-model="attributeName">
  <option value="placeholder">placeholder</option>
  <option value="title">title</option>
  <option value="data-info">data-info</option>
</select>
<input v-model="attributeValue" placeholder="属性值" />

// JavaScript代码
const app = createApp({
  setup() {
    const attributeName = ref('placeholder')
    const attributeValue = ref('动态属性值')
    
    return {
      attributeName,
      attributeValue
    }
  }
})
app.mount('#dynamic-attr-demo')
        </textarea>
      </div>

      <div class="performance-note">
        <strong>高级用法：</strong>动态属性名在构建通用组件时特别有用，可以根据不同情况绑定不同的属性。
      </div>
    </div>

    <!-- 最佳实践 -->
    <div class="container">
      <h2>最佳实践与注意事项</h2>
      <ul>
        <li><strong>使用简写语法：</strong>推荐使用<code>:</code>代替<code>v-bind:</code></li>
        <li><strong>布尔属性处理：</strong>理解布尔属性的特殊绑定规则</li>
        <li><strong>性能考虑：</strong>避免在属性绑定中使用复杂的表达式</li>
        <li><strong>安全性：</strong>注意XSS攻击，不要绑定用户输入的HTML</li>
        <li><strong>命名规范：</strong>使用kebab-case命名HTML属性</li>
      </ul>
    </div>

    <script>
      const { createApp, ref, computed } = Vue

      // 1. 基础属性绑定应用
      const basicBindingApp = createApp({
        setup() {
          const images = [
            'https://picsum.photos/200/150?random=1',
            'https://picsum.photos/200/150?random=2',
            'https://picsum.photos/200/150?random=3'
          ]
          const currentImageIndex = ref(0)
          const currentImage = computed(() => images[currentImageIndex.value])
          const imageAlt = computed(() => `示例图片 ${currentImageIndex.value + 1}`)

          const changeImage = () => {
            currentImageIndex.value = (currentImageIndex.value + 1) % images.length
          }

          return {
            currentImage,
            imageAlt,
            changeImage
          }
        }
      })
      basicBindingApp.mount('#basic-binding-demo')

      // 2. 链接和表单属性绑定应用
      const linkFormApp = createApp({
        setup() {
          // 链接相关
          const dynamicUrl = ref('https://vuejs.org')
          const openInNewTab = ref(false)
          const linkTarget = computed(() => openInNewTab.value ? '_blank' : '_self')
          const linkText = computed(() => `访问 ${dynamicUrl.value}`)

          // 表单相关
          const inputType = ref('text')
          const inputPlaceholder = computed(() => {
            const placeholders = {
              text: '请输入文本',
              password: '请输入密码',
              email: '请输入邮箱',
              number: '请输入数字'
            }
            return placeholders[inputType.value]
          })
          const isDisabled = ref(false)
          const isReadonly = ref(false)
          const inputValue = ref('')

          return {
            dynamicUrl,
            openInNewTab,
            linkTarget,
            linkText,
            inputType,
            inputPlaceholder,
            isDisabled,
            isReadonly,
            inputValue
          }
        }
      })
      linkFormApp.mount('#link-form-demo')

      // 3. 布尔属性绑定应用
      const booleanApp = createApp({
        setup() {
          const isButtonDisabled = ref(false)
          const isChecked = ref(false)
          const isRequired = ref(false)

          return {
            isButtonDisabled,
            isChecked,
            isRequired
          }
        }
      })
      booleanApp.mount('#boolean-demo')

      // 4. 多属性绑定应用
      const multipleAttrsApp = createApp({
        setup() {
          const multipleAttrs = ref({
            id: 'multi-attrs',
            class: 'highlight',
            style: 'padding: 10px; background: lightblue;',
            'data-info': '多属性绑定示例'
          })

          const updateAttrs = () => {
            multipleAttrs.value = {
              ...multipleAttrs.value,
              style: `padding: 15px; background: ${Math.random() > 0.5 ? 'lightcoral' : 'lightgreen'};`,
              'data-info': `更新时间: ${new Date().toLocaleTimeString()}`
            }
          }

          return {
            multipleAttrs,
            updateAttrs
          }
        }
      })
      multipleAttrsApp.mount('#multiple-attrs-demo')

      // 5. 动态属性名应用
      const dynamicAttrApp = createApp({
        setup() {
          const attributeName = ref('placeholder')
          const attributeValue = ref('请输入内容')

          // 监听属性名变化，自动设置合适的默认值
          const { watch } = Vue
          watch(attributeName, (newAttrName) => {
            switch(newAttrName) {
              case 'placeholder':
                attributeValue.value = '请输入内容'
                break
              case 'title':
                attributeValue.value = '这是一个提示标题'
                break
              case 'data-info':
                attributeValue.value = '自定义数据信息'
                break
              case 'maxlength':
                attributeValue.value = '10'
                break
              default:
                attributeValue.value = '默认值'
            }
          })

          return {
            attributeName,
            attributeValue
          }
        }
      })
      dynamicAttrApp.mount('#dynamic-attr-demo')
    </script>
  </body>
</html>